import { Window } from "@/windows/Window.js";
import { ColorExtraction } from "@/utils/ColorExtraction.js";
import { TextManager } from "@/managers/TextManager.js";
import { ImageManager } from "@/managers/ImageManager.js";
import { isBigCharacter } from "@/utils/ImageFileType.js";

export class Window_Base extends Window {
    static _iconWidth=32;
    static _iconHeight=32;
    static _faceWidth=144;
    static _faceHeight=144;
    constructor(x, y, width, height) {
        super(width,height);
        this.move(x, y);
        this._opening = false;
        this._closing = false;
        //this._dimmerSprite = null;
    }
    lineHeight() {return 36;}
    standardFontSize() {return 28;}
    standardPadding() {return 18;}
    textPadding() {return 6;}
    standardBackOpacity() {return 192;}
    contentsWidth() {return this.width - this.standardPadding() * 2;}
    contentsHeight() {return this.height - this.standardPadding() * 2;}
    fittingHeight(numLines) {return numLines * this.lineHeight() + this.standardPadding() * 2;}
    resetTextColor() {this.changeTextColor(ColorExtraction.normalColor());}
    update() {
        super.update();
        this.updateOpen();
        this.updateClose();
        //this.updateBackgroundDimmer();
    }
    updateOpen() {
        if (this._opening) {
            this.openness += 32;
            if (this.isOpen()) {
                this._opening = false;
            }
        }
    }

    updateClose() {
        if (this._closing) {
            this.openness -= 32;
            if (this.isClosed()) {
                this._closing = false;
            }
        }
    }

    isOpening() {return this._opening;}
    isClosing() {return this._closing;}
    show() {this.visible = true;}
    hide() {this.visible = false;}
    activate() {this.active = true;}
    deactivate() {this.active = false;}
    translucentOpacity() {return 160;}
    changeTextColor(color) {this.contents.textColor = color;}
    changePaintOpacity(enabled) {this.contents.paintOpacity = enabled ? 255 : this.translucentOpacity();}
    drawText(text, x, y, maxWidth, align) {this.contents.drawText(text, x, y, maxWidth, this.lineHeight(), align);}
    textWidth(text) {return this.contents.measureTextWidth(text);}
    drawTextEx(text, x, y) {
        if (text) {
            let textState = { index: 0, x: x, y: y, left: x };
            textState.text = this.convertEscapeCharacters(text);
            textState.height = this.calcTextHeight(textState, false);
            this.resetFontSettings();
            while (textState.index < textState.text.length) {
                this.processCharacter(textState);
            }
            return textState.x - x;
        } else {
            return 0;
        }
    }

    convertEscapeCharacters(text) {
        text = text.replace(/\\/g, '\x1b');
        text = text.replace(/\x1b\x1b/g, '\\');
        text = text.replace(/\x1bV\[(\d+)\]/gi, function() {
            return $gameVariables.value(parseInt(arguments[1]));
        });
        text = text.replace(/\x1bV\[(\d+)\]/gi, function() {
            return $gameVariables.value(parseInt(arguments[1]));
        });
        text = text.replace(/\x1bN\[(\d+)\]/gi, function() {
            return this.actorName(parseInt(arguments[1]));
        });
        text = text.replace(/\x1bP\[(\d+)\]/gi, function() {
            return this.partyMemberName(parseInt(arguments[1]));
        });
        text = text.replace(/\x1bG/gi, TextManager.currencyUnit);
        return text;
    }

    actorName(n) {
        const actor = n >= 1 ? $gameActors.actor(n) : null;
        return actor ? actor.name() : '';
    }

    partyMemberName(n) {
        const actor = n >= 1 ? $gameParty.members()[n - 1] : null;
        return actor ? actor.name() : '';
    }

    processCharacter(textState) {
        switch (textState.text[textState.index]) {
        case '\n':
            this.processNewLine(textState);
            break;
        case '\f':
            this.processNewPage(textState);
            break;
        case '\x1b':
            this.processEscapeCharacter(this.obtainEscapeCode(textState), textState);
            break;
        default:
            this.processNormalCharacter(textState);
            break;
        }
    }

    processNormalCharacter(textState) {
        const c = textState.text[textState.index++];
        const w = this.textWidth(c);
        this.contents.drawText(c, textState.x, textState.y, w * 2, textState.height);
        textState.x += w;
    }

    processNewLine(textState) {
        textState.x = textState.left;
        textState.y += textState.height;
        textState.height = this.calcTextHeight(textState, false);
        textState.index++;
    }

    processNewPage(textState) {
        textState.index++;
    }

    obtainEscapeCode(textState) {
        textState.index++;
        const regExp = /^[\$\.\|\^!><\{\}\\]|^[A-Z]+/i;
        const arr = regExp.exec(textState.text.slice(textState.index));
        if (arr) {
            textState.index += arr[0].length;
            return arr[0].toUpperCase();
        } else {
            return '';
        }
    }

    obtainEscapeParam(textState) {
        const arr = /^\[\d+\]/.exec(textState.text.slice(textState.index));
        if (arr) {
            textState.index += arr[0].length;
            return parseInt(arr[0].slice(1));
        } else {
            return '';
        }
    }

    processEscapeCharacter(code, textState) {
        switch (code) {
        case 'C':
            this.changeTextColor(ColorExtraction.textColor(this.obtainEscapeParam(textState)));
            break;
        case 'I':
            this.processDrawIcon(this.obtainEscapeParam(textState), textState);
            break;
        case '{':
            this.makeFontBigger();
            break;
        case '}':
            this.makeFontSmaller();
            break;
        }
    }

    processDrawIcon(iconIndex, textState) {
        this.drawIcon(iconIndex, textState.x + 2, textState.y + 2);
        textState.x += Window_Base._iconWidth + 4;
    }

    makeFontBigger() {
        if (this.contents.fontSize <= 96) {
            this.contents.fontSize += 12;
        }
    }

    makeFontSmaller() {
        if (this.contents.fontSize >= 24) {
            this.contents.fontSize -= 12;
        }
    }

    calcTextHeight(textState, all) {
        const lastFontSize = this.contents.fontSize;
        let textHeight = 0;
        const lines = textState.text.slice(textState.index).split('\n');
        const maxLines = all ? lines.length : 1;

        for (let i = 0; i < maxLines; i++) {
            let maxFontSize = this.contents.fontSize;
            const regExp = /\x1b[\{\}]/g;
            for (;;) {
                const array = regExp.exec(lines[i]);
                if (array) {
                    if (array[0] === '\x1b{') {
                        this.makeFontBigger();
                    }
                    if (array[0] === '\x1b}') {
                        this.makeFontSmaller();
                    }
                    if (maxFontSize < this.contents.fontSize) {
                        maxFontSize = this.contents.fontSize;
                    }
                } else {
                    break;
                }
            }
            textHeight += maxFontSize + 8;
        }

        this.contents.fontSize = lastFontSize;
        return textHeight;
    }

    drawIcon(iconIndex, x, y) {
       this.contents.drawIcon(iconIndex,x,y);
    }

    drawFace(faceName, faceIndex, x, y, width, height) {
        width = width || Window_Base._faceWidth;
        height = height || Window_Base._faceHeight;
        let pw = Window_Base._faceWidth;
        let ph = Window_Base._faceHeight;
        let sw = Math.min(width, pw);
        let sh = Math.min(height, ph);
        let dx = Math.floor(x + Math.max(width - pw, 0) / 2);
        let dy = Math.floor(y + Math.max(height - ph, 0) / 2);
        let sx = faceIndex % 4 * pw + (pw - sw) / 2;
        let sy = Math.floor(faceIndex / 4) * ph + (ph - sh) / 2;
        ImageManager.loadFace(faceName).then(source=>{
            if(source){
                let resource=source.resource;
                this.contents.blt(resource, sx, sy, sw, sh, dx, dy);
                ImageManager.releaseFace(faceName);
            } 
        });
    }

    drawCharacter(characterName, characterIndex, x, y) {
        let big = isBigCharacter(characterName);
        let pw = bitmap.width / (big ? 3 : 12);
        let ph = bitmap.height / (big ? 4 : 8);
        let n = characterIndex;
        let sx = (n % 4 * 3 + 1) * pw;
        let sy = (Math.floor(n / 4) * 4) * ph;
        ImageManager.loadCharacter(characterName).then(source=>{
            this.contents.blt(source.resource, sx, sy, pw, ph, x - pw / 2, y - ph);
            ImageManager.releaseCharacter(characterName);
        });
    }

    drawGauge(x, y, width, rate, color1, color2) {
        let fillW = Math.floor(width * rate);
        let gaugeY = y + this.lineHeight() - 8;
        this.contents.fillRect(x, gaugeY, width, 6, ColorExtraction.gaugeBackColor());
        this.contents.gradientFillRect(x, gaugeY, fillW, 6, color1, color2);
    }
    drawActorCharacter(actor, x, y) {
        this.drawCharacter(actor.characterName(), actor.characterIndex(), x, y);
    }

    drawActorFace(actor, x, y, width, height) {
        this.drawFace(actor.faceName(), actor.faceIndex(), x, y, width, height);
    }

    drawActorName(actor, x, y, width) {
        width = width || 168;
        this.changeTextColor(ColorExtraction.hpColor(actor));
        this.drawText(actor.name(), x, y, width);
    }

    drawActorClass(actor, x, y, width) {
        width = width || 168;
        this.resetTextColor();
        this.drawText(actor.currentClass().name, x, y, width);
    }

    drawActorNickname(actor, x, y, width) {
        width = width || 270;
        this.resetTextColor();
        this.drawText(actor.nickname(), x, y, width);
    }

    drawActorLevel(actor, x, y) {
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.levelA, x, y, 48);
        this.resetTextColor();
        this.drawText(actor.level, x + 84, y, 36, 'right');
    }

    drawActorIcons(actor, x, y, width) {
        width = width || 144;
        const icons = actor.allIcons().slice(0, Math.floor(width / Window_Base._iconWidth));
        for (let i = 0; i < icons.length; i++) {
            this.drawIcon(icons[i], x + Window_Base._iconWidth * i, y + 2);
        }
    }

    drawCurrentAndMax(current, max, x, y, width, color1, color2) {
        let labelWidth = this.textWidth('HP');
        let valueWidth = this.textWidth('0000');
        let slashWidth = this.textWidth('/');
        let x1 = x + width - valueWidth;
        let x2 = x1 - slashWidth;
        let x3 = x2 - valueWidth;
        if (x3 >= x + labelWidth) {
            this.changeTextColor(color1);
            this.drawText(current, x3, y, valueWidth, 'right');
            this.changeTextColor(color2);
            this.drawText('/', x2, y, slashWidth, 'right');
            this.drawText(max, x1, y, valueWidth, 'right');
        } else {
            this.changeTextColor(color1);
            this.drawText(current, x1, y, valueWidth, 'right');
        }
    }

    drawActorHp(actor, x, y, width) {
        width = width || 186;
        const color1 = ColorExtraction.hpGaugeColor1();
        const color2 = ColorExtraction.hpGaugeColor2();
        this.drawGauge(x, y, width, actor.hpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.hpA, x, y, 44);
        this.drawCurrentAndMax(actor.hp, actor.mhp, x, y, width,
                               ColorExtraction.hpColor(actor), 
                               ColorExtraction.normalColor());
    }

    drawActorMp(actor, x, y, width) {
        width = width || 186;
        const color1 = ColorExtraction.mpGaugeColor1();
        const color2 = ColorExtraction.mpGaugeColor2();
        this.drawGauge(x, y, width, actor.mpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.mpA, x, y, 44);
        this.drawCurrentAndMax(actor.mp, actor.mmp, x, y, width,
                               ColorExtraction.mpColor(actor), 
                               ColorExtraction.normalColor());
    }

    drawActorTp(actor, x, y, width) {
        width = width || 96;
        const color1 = ColorExtraction.tpGaugeColor1();
        const color2 = ColorExtraction.tpGaugeColor2();
        this.drawGauge(x, y, width, actor.tpRate(), color1, color2);
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(TextManager.tpA, x, y, 44);
        this.changeTextColor(ColorExtraction.tpColor(actor));
        this.drawText(actor.tp, x + width - 64, y, 64, 'right');
    }

    drawActorSimpleStatus(actor, x, y, width) {
        const lineHeight = this.lineHeight();
        const x2 = x + 180;
        const width2 = Math.min(200, width - 180 - this.textPadding());
        this.drawActorName(actor, x, y);
        this.drawActorLevel(actor, x, y + lineHeight * 1);
        this.drawActorIcons(actor, x, y + lineHeight * 2);
        this.drawActorClass(actor, x2, y);
        this.drawActorHp(actor, x2, y + lineHeight * 1, width2);
        this.drawActorMp(actor, x2, y + lineHeight * 2, width2);
    }

    drawItemName(item, x, y, width) {
        width = width || 312;
        if (item) {
            const iconBoxWidth = Window_Base._iconWidth + 4;
            this.resetTextColor();
            this.drawIcon(item.iconIndex, x + 2, y + 2);
            this.drawText(item.name, x + iconBoxWidth, y, width - iconBoxWidth);
        }
    }

    drawCurrencyValue(value, unit, x, y, width) {
        const unitWidth = Math.min(80, this.textWidth(unit));
        this.resetTextColor();
        this.drawText(value, x, y, width - unitWidth - 6, 'right');
        this.changeTextColor(ColorExtraction.systemColor());
        this.drawText(unit, x + width - unitWidth, y, unitWidth, 'right');
    }

    setBackgroundType(type) {
        // if (type === 0) {
        //     this.opacity = 255;
        // } else {
        //     this.opacity = 0;
        // }
        // if (type === 1) {
        //     this.showBackgroundDimmer();
        // } else {
        //     this.hideBackgroundDimmer();
        // }
    }

    showBackgroundDimmer() {
        // if (!this._dimmerSprite) {
        //     this._dimmerSprite = new Sprite();
        //     this._dimmerSprite.bitmap = new Bitmap(0, 0);
        //     this.addChildToBack(this._dimmerSprite);
        // }
        // const bitmap = this._dimmerSprite.bitmap;
        // if (bitmap.width !== this.width || bitmap.height !== this.height) {
        //     this.refreshDimmerBitmap();
        // }
        // this._dimmerSprite.visible = true;
        // this.updateBackgroundDimmer();
    }

    hideBackgroundDimmer() {
        // if (this._dimmerSprite) {
        //     this._dimmerSprite.visible = false;
        // }
    }

    updateBackgroundDimmer() {
        // if (this._dimmerSprite) {
        //     this._dimmerSprite.opacity = this.openness;
        // }
    }

    refreshDimmerBitmap() {
        // if (this._dimmerSprite) {
        //     let bitmap = this._dimmerSprite.bitmap;
        //     let w = this.width;
        //     let h = this.height;
        //     let m = this.padding;
        //     let c1 = this.dimColor1();
        //     let c2 = this.dimColor2();
        //     bitmap.resize(w, h);
        //     bitmap.gradientFillRect(0, 0, w, m, c2, c1, true);
        //     bitmap.fillRect(0, m, w, h - m * 2, c1);
        //     bitmap.gradientFillRect(0, h - m, w, m, c1, c2, true);
        //     this._dimmerSprite.setFrame(0, 0, w, h);
        // }
    }

    canvasToLocalX(x) {
        let node = this;
        while (node) {
            x -= node.x;
            node = node.parent;
        }
        return x;
    }

    canvasToLocalY(y) {
        let node = this;
        while (node) {
            y -= node.y;
            node = node.parent;
        }
        return y;
    }
    
    reserveFaceImages() {}
}

